/*
 * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vladzzag@gmail.com>
 *
 * SPDX-License-Identifier: GPL-3.0-or-later
 */

// Own
#include "dynamicwallpaperpackage.h"

// KF
#include <KLocalizedString>
#include <KPackage/PackageLoader>

// Qt
#include <QJsonArray>
#include <QJsonObject>

WallpaperType DynamicWallpaperPackage::type() const
{
    return m_type;
}

bool DynamicWallpaperPackage::isSmooth() const
{
    return m_isSmooth;
}

QVector<WallpaperImage> DynamicWallpaperPackage::images() const
{
    return m_images;
}

static QJsonObject extractWallpaperObject(const KPackage::Package &package)
{
    const QJsonObject &rootObject = package.metadata().rawData();
    if (rootObject.isEmpty())
        return QJsonObject();

    return rootObject.value(QLatin1String("Wallpaper")).toObject();
}

static QJsonArray extractMetaData(const KPackage::Package &package)
{
    const QJsonObject &wallpaperObject = extractWallpaperObject(package);
    if (wallpaperObject.isEmpty())
        return QJsonArray();

    return wallpaperObject.value(QLatin1String("MetaData")).toArray();
}

static bool parseWallpaperType(const KPackage::Package &package, WallpaperType &type, QString &errorText)
{
    const QJsonObject wallpaperObject = extractWallpaperObject(package);
    if (wallpaperObject.isEmpty()) {
        errorText = i18n("Metadata has no object 'Wallpaper'.");
        return false;
    }

    const QJsonValue value = wallpaperObject.value(QStringLiteral("Type"));
    if (value.isUndefined()) {
        // Type is not set explicitly, assume that's a solar dynamic wallpaper.
        type = WallpaperType::Solar;
        return true;
    }

    const static QHash<QString, WallpaperType> supportedWallpaperTypes {
        { QStringLiteral("solar"), WallpaperType::Solar },
        { QStringLiteral("timed"), WallpaperType::Timed },
    };

    const auto typeIt = supportedWallpaperTypes.constFind(value.toString());
    if (typeIt == supportedWallpaperTypes.constEnd()) {
        errorText = i18n("Unknown wallpaper type: '%1'. Supported wallpaper types: %2.",
                         value.toString(), supportedWallpaperTypes.keys().join(QLatin1String(", ")));
        return false;
    }

    type = *typeIt;
    return true;
}

static bool parseSolarMetaData(const KPackage::Package &package, QVector<WallpaperImage> &images, QString &errorText)
{
    const QJsonArray metaData = extractMetaData(package);

    images.reserve(metaData.count());

    for (int i = 0; i < metaData.count(); ++i) {
        const QJsonObject rawImage = metaData.at(i).toObject();
        if (rawImage.isEmpty()) {
            errorText = i18n("Image with index %1 has no metadata associated with it.", i);
            return false;
        }

        const QJsonValue rawAzimuth = rawImage.value(QLatin1String("Azimuth"));
        if (!rawAzimuth.isDouble()) {
            errorText = i18n("Image with index %1 has an invalid value for key 'Azimuth': '%2'. "
                             "The azimuth must be a real number between 0 and 360 inclusive.",
                             i, rawAzimuth.toString());
            return false;
        }
        const QJsonValue rawElevation = rawImage.value(QLatin1String("Elevation"));
        if (!rawElevation.isDouble()) {
            errorText = i18n("Image with index %1 has an invalid value for key 'Elevation': '%2'. "
                             "The elevation must be a real number between -90 and 90 inclusive.",
                             i, rawElevation.toString());
            return false;
        }

        WallpaperImage image = {};
        image.position = SunPosition(rawElevation.toDouble(), rawAzimuth.toDouble());

        const QString fileName = rawImage.value(QLatin1String("FileName")).toString();
        const QUrl url = package.fileUrl(QByteArrayLiteral("images"), fileName);
        image.url = url;

        images << image;
    }

    return true;
}

static bool parseTimedMetaData(const KPackage::Package &package, QVector<WallpaperImage> &images, QString &errorText)
{
    const QJsonArray metaData = extractMetaData(package);

    images.reserve(metaData.count());

    for (int i = 0; i < metaData.count(); ++i) {
        const QJsonObject rawImage = metaData.at(i).toObject();
        if (rawImage.isEmpty()) {
            errorText = i18n("Image with index %1 has no metadata associated with it.", i);
            return false;
        }

        const QJsonValue rawTime = rawImage.value(QLatin1String("Time"));
        if (!rawTime.isDouble()) {
            errorText = i18n("Image with index %1 has an invalid value for key 'Time': '%2'. "
                             "The time must be a real number between 0 and 1 inclusive.",
                             i, rawTime.toString());
            return false;
        }
        const qreal time = rawTime.toDouble();
        if (time < 0 || time > 1) {
            errorText = i18n("Image with index %1 has an invalid value for key 'Time': '%2'. "
                             "The time must be a real number between 0 and 1 inclusive.",
                             i, rawTime.toString());
            return false;
        }

        WallpaperImage image = {};
        image.time = time;

        const QString fileName = rawImage.value(QLatin1String("FileName")).toString();
        const QUrl url = package.fileUrl(QByteArrayLiteral("images"), fileName);
        image.url = url;

        images << image;
    }

    return true;
}

static void parseSmoothMode(const KPackage::Package &package, bool &smooth)
{
    const QJsonObject wallpaperObject = extractWallpaperObject(package);
    const QJsonValue smoothValue = wallpaperObject.value(QLatin1String("Smooth"));

    // If the Smooth key is not present, assume that it's implicitly set to true.
    smooth = smoothValue.isUndefined() || smoothValue.toBool();
}

bool DynamicWallpaperLoader::load(const QString &id)
{
    reset();

    const KPackage::Package package = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Wallpaper/Dynamic"), id);
    if (!package.isValid()) {
        m_errorText = i18n("No such dynamic wallpaper or invalid metadata.");
        return false;
    }

    auto wallpaper = std::make_shared<DynamicWallpaperPackage>();
    if (!parseWallpaperType(package, wallpaper->m_type, m_errorText))
        return false;

    parseSmoothMode(package, wallpaper->m_isSmooth);

    switch (wallpaper->type()) {
    case WallpaperType::Solar:
        if (!parseSolarMetaData(package, wallpaper->m_images, m_errorText))
            return false;
        break;
    case WallpaperType::Timed:
        if (!parseTimedMetaData(package, wallpaper->m_images, m_errorText))
            return false;
        break;
    default:
        Q_UNREACHABLE();
    }

    if (wallpaper->m_images.count() < 2) {
        m_errorText = i18n("A dynamic wallpaper must have at least two images, %1 provided.",
                           wallpaper->m_images.count());
        return false;
    }

    m_wallpaper = wallpaper;

    return true;
}

QString DynamicWallpaperLoader::errorText() const
{
    return m_errorText;
}

std::shared_ptr<DynamicWallpaperPackage> DynamicWallpaperLoader::wallpaper() const
{
    return m_wallpaper;
}

void DynamicWallpaperLoader::reset()
{
    m_errorText = QString();
    m_wallpaper = {};
}
